package com.tencent.sr.iris.activity.service.event.activity;


import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.tencent.sr.iris.activity.common.enums.EventHandingRecordStatusEnum;
import com.tencent.sr.iris.activity.dependency.user.UserLabelDependency;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityCheckResultDTO;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityDTO;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityTaskDTO;
import com.tencent.sr.iris.activity.interfaces.event.dto.ActivityEventDTO;
import com.tencent.sr.iris.activity.repository.entity.TIrisEventHandingRecordDO;
import com.tencent.sr.iris.activity.service.activity.IrisEventHandingRecordService;
import com.tencent.sr.iris.activity.service.activity.IrisUserActivityRecordService;
import com.tencent.sr.iris.activity.service.event.EventExecutorContext;
import com.tencent.sr.iris.activity.service.event.task.IrisTaskExecutor;
import com.tencent.sr.rmall.common.exception.retail.TRetailBizException;
import com.tencent.sr.rmall.common.exception.retail.TRetailErrorException;
import com.tencent.sr.rmall.springbootstarter.monitor.util.MonitorUtil;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * 活动处理模版类
 *
 * @author xiaojun
 */
@Slf4j
public abstract class AbstractIrisActivityExecutor implements IrisActivityExecutor {

    @Resource
    IrisEventHandingRecordService irisEventHandingRecordService;

    @Resource
    private EventExecutorContext eventExecutorContext;

    @Resource
    protected IrisUserActivityRecordService activityRecordService;

    /**
     * 任务处理器
     *
     * @param eventDTO
     * @param act
     */
    @Override
    public void execute(ActivityEventDTO eventDTO, ActivityDTO act) {

        Throwable throwable = null;
        long startTime = System.currentTimeMillis();
        List<String> eventHandingInfoList = Lists.newArrayList();
        TIrisEventHandingRecordDO handingRecordDO = null;
        try {
            //先保存处理流水
            handingRecordDO = irisEventHandingRecordService.queryRecord(eventDTO, act, null);
            if (handingRecordDO == null) {
                handingRecordDO = irisEventHandingRecordService.saveHandingRecord(eventDTO, act, null,
                        eventHandingInfoList);
            }

            //前置处理
            before(eventDTO, act);
            //处理事件
            process(eventDTO, act, eventHandingInfoList);
            //后置处理
            after(eventDTO, act);

            handingRecordDO.setHandingInfo(JSON.toJSONString(eventHandingInfoList));
            handingRecordDO.setHandingStatus(EventHandingRecordStatusEnum.SUCCEES.getCode());
        } catch (TRetailBizException e) {
            //业务异常只做中断，不重试
            eventHandingInfoList.add(e.getMessage());
            handingRecordDO.setHandingInfo(JSON.toJSONString(eventHandingInfoList));
            handingRecordDO.setHandingStatus(EventHandingRecordStatusEnum.SUCCEES.getCode());
            log.info("活动处理业务异常 eventIdempotent:{},actBizId:{}", eventDTO.getIdempotent(), act.getBizId(), e);
        } catch (TRetailErrorException e) {
            //业务错误中断，需要重试
            eventHandingInfoList.add(e.getMessage());
            handingRecordDO.setHandingInfo(JSON.toJSONString(eventHandingInfoList));
            handingRecordDO.setFailCount((byte) (handingRecordDO.getFailCount() + 1));
            handingRecordDO.setHandingStatus(EventHandingRecordStatusEnum.FAIL.getCode());
            throwable = e;
            log.error("活动处理业务错误 eventIdempotent:{},actBizId:{}", eventDTO.getIdempotent(), act.getBizId(), e);
        } catch (Exception e) {
            //系统异常，需要重试
            eventHandingInfoList.add("系统异常");
            handingRecordDO.setHandingInfo(JSON.toJSONString(eventHandingInfoList));
            handingRecordDO.setFailCount((byte) (handingRecordDO.getFailCount() + 1));
            handingRecordDO.setHandingStatus(EventHandingRecordStatusEnum.FAIL.getCode());
            throwable = e;
            log.error("活动处理系统异常 eventIdempotent:{},actBizId:{}", eventDTO.getIdempotent(), act.getBizId(), e);
        } finally {
            irisEventHandingRecordService.updateRecord(handingRecordDO);
            log.info("活动处理：actId:{},耗时：{}", act.getBizId(), (System.currentTimeMillis() - startTime));
            MonitorUtil.getSingleMonitorService().logTransaction("activity_event_handler", "activity",
                    startTime, throwable);
        }

    }

    protected abstract void after(ActivityEventDTO eventDTO, ActivityDTO act);

    protected void process(ActivityEventDTO eventDTO, ActivityDTO act, List<String> eventHandingInfoList) {
        //查找对应的任务
        List<ActivityTaskDTO> taskList = getTask(eventDTO, act);
        if (CollectionUtil.isEmpty(taskList)) {
            eventHandingInfoList.add("活动处理：没有对应的任务");
            return;
        }
        taskList.stream().forEach(task -> {
            //查找对应的任务处理器
            IrisTaskExecutor irisTaskExecutor = getTaskExecutor(eventDTO, act, task);
            irisTaskExecutor.execute(eventDTO, act, task, null);
        });

    }

    protected abstract List<ActivityTaskDTO> getTask(ActivityEventDTO eventDTO, ActivityDTO act);

    protected IrisTaskExecutor getTaskExecutor(ActivityEventDTO eventDTO, ActivityDTO act, ActivityTaskDTO task) {
        return eventExecutorContext.getTaskHandler(task.getBizId());
    }

    private void before(ActivityEventDTO eventDTO, ActivityDTO act) {

        Date eventDateTime = new Date(eventDTO.getEventTime());
        if (eventDateTime.before(act.getStartTime()) || eventDateTime.after(act.getEndTime())) {
            log.info("活动处理：事件时间不在活动时间范围内，不处理，eventDTO:{},act:{}", JSON.toJSONString(eventDTO),
                    JSON.toJSONString(act));
            throw new TRetailBizException("活动处理：事件时间不在活动时间范围内，不处理");
        }
        check(eventDTO, act);
    }

    /**
     * 扩展活动校验
     *
     * @param eventDTO
     * @param act
     */
    protected abstract void check(ActivityEventDTO eventDTO, ActivityDTO act);

    /**
     * 活动基础校验
     *
     * @param activity
     * @param msg
     * @return
     */
    public ActivityCheckResultDTO doActivityCheck(ActivityDTO activity, ActivityEventDTO msg) {
        return ActivityCheckResultDTO.builder().satisfy(true).build();
    }

    /**
     * 用户活动准入规则判断
     *
     * @param activity
     * @param msg
     * @return
     */
    public ActivityCheckResultDTO doAccessConditionCheck(ActivityDTO activity, ActivityEventDTO msg) {
        return ActivityCheckResultDTO.builder().satisfy(true).build();
    }

}
